/**
 * @file        main.c
 * @brief       XXXX
 * @note        XXXX
 * @author      靳普诏(puzhao.jin@hopechart.com)
 * @date        2023/11/11
 * @version     1.0

 * @par         修改日志
 * <table>
 * <tr><th>Date         <th>Version     <th>Author      <th> Description
 * <tr><td>2023/11/11   <td>1.0         <td>靳普诏       <td> 创建初始版本
 * @copyright 杭州鸿泉物联网技术股份有限公司
 */
 
#include "main.h"

#include "IniReadStr.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <malloc.h>
#include <windows.h>

static THeadInfo g_head_info_ = {0};

static char g_ref_dir[128];
static char g_ref_file_list[256];
static char g_ref_filename[256];

static char g_output_dir[128];
static char g_output_filename[256];

static char g_wr_mem[1024];

/*----------------------------------读取配置相关-----------------------------------------*/
// 读取配置文件
static int L_ReadCfgStr(const char *section, const char *key, char *value, unsigned int value_size)
{
    int len = IniReadStr(CONFIG_INI_FILENAME, section, key, value, value_size);
    
    if ((unsigned)len < value_size)
        value[len] = '\0';
    else
    {
        printf("IniReadStr Failed ret:%d, ini:%s, section:%s, key:%s",len, CONFIG_INI_FILENAME, section, key);
    }
    return len;
}

static char *L_ReadCfgStr_BuildFilename(void)
{
    int len1 = L_ReadCfgStr("build", "build_dir", g_output_dir, sizeof(g_output_dir));

    if (len1 > 0)
    {
        memcpy(g_output_filename, g_output_dir, len1);
        g_output_filename[len1] = '\\';
        len1++;

        int len2 = L_ReadCfgStr("build", "build_filename", &g_output_filename[len1], sizeof(g_output_filename) - len1);
    }
    else
    {
        printf("Build Dir Get Failed!");
    }

    return g_output_filename;
}

static char *L_ReadCfgStr_RefFilename(unsigned char index)
{
    char *ref_file = NULL;
    unsigned char index_cnt = 1;

    if (index == 0)
    {
        int len1 = L_ReadCfgStr("ref", "ref_dir", g_ref_dir, sizeof(g_ref_dir) - 1);
        if (len1 > 0)
            g_ref_dir[len1] = '\0';

        len1 = L_ReadCfgStr("ref", "ref_file_list", g_ref_file_list, sizeof(g_ref_file_list) - 1);
        if (len1 > 0)
            g_ref_dir[len1] = '\0';
    }
    
    char *pos = g_ref_file_list;
    char *end = pos + strlen(g_ref_file_list) - 1;
    unsigned char i = 0;

    ref_file = pos;

    while (pos < end)
    {
        if (*pos == ',')
        {
            if (i == index)
            {
                end = pos - 1;
                break;
            }
            else
            {
                i++;
                ref_file = pos + 1;
            }
            index_cnt++;
        }
        pos++;
    }

    if (index < index_cnt)
    {
        while ((unsigned)*ref_file <= ' ' && *ref_file != '\0')
            ++ref_file;

        while ((unsigned)*end <= ' ' && end > ref_file)
            --end;

        if (ref_file == end)
            ref_file = NULL;
        else
        {
            int len1 = strlen(g_ref_dir);
            int len2 = end - ref_file + 1;

            memcpy(g_ref_filename, g_ref_dir, len1);
            g_ref_filename[len1] = '\\';
            len1++;
            memcpy(&g_ref_filename[len1], ref_file, len2);
            g_ref_filename[len1 + len2] = '\0';

            ref_file = g_ref_filename;
        }
    }
    else
    {
        ref_file = NULL;
    }
    
    return ref_file;
}

/*---------------------------------------- crc 计算相关 -----------------------------------------------*/

static int L_CalcFileCrc16(const char *filename, UInt16 *crc)
{
    int result = -1;
    
    if (filename != NULL && crc != NULL)
    {
        FILE *fp = fopen(filename, "rb");
        *crc = 0xffff;

        if (fp != NULL)
        {
            UInt16 seed = 0xffff;
            int size;
            int filesize = 0;

            fseek(fp, 0, SEEK_SET);
            // TODO 补充crc算法
            size = fread(g_wr_mem, 1, sizeof(g_wr_mem), fp);

            while (size > 0)
            {
                filesize += size;
                seed = OfCalcCrc16s(g_wr_mem, size, seed);

                size = fread(g_wr_mem, 1, sizeof(g_wr_mem), fp);
            }

            //  fseek(fp, 0, SEEK_END);
            *crc = seed;
            result = filesize;
            fclose(fp);
        }
        else
        {
            result = -2;
        }
    }


    return result;
}

/*---------------------------------------------------------------------------------------*/

// 加载头部信息 >> g_head_info_
int LoadHeadInfo(THeadInfo *head)
{
    TBtsFwTransAppStorage *info = &head->info;
    
    memset(head, 0, sizeof(*head)); // 全部数据默认使用 0 填充
    printf("g_head_info size: 0x%x\n", sizeof(g_head_info_));

    
    info->tag = 0x55AABBCC; // 标签，用作检查。固定填充

    info->len = sizeof(info->fw_size) + sizeof(info->crc16_value);  // TODO 头部信息数据长度


    char *ref_filename = L_ReadCfgStr_RefFilename(0);
    UInt16 crc;

    info->fw_size = L_CalcFileCrc16(ref_filename, &crc);  // TODO 固件大小
    info->crc16_value = crc; // TODO 计算固件 crc
    printf("firmware size: %d Byte, crc16: 0x%x\n", info->fw_size, info->crc16_value);

    info->crc = OfCalcCrc16s(&info->len, info->len + sizeof(info->len), 0xffff);  // 头部crc计算



    return 0;
}

/**
 * 反转内存
 * @param *data
 * @param size
 * @return 
 */
static void L_ReverseBin(char *data, int size)
{
    if (size > 1)
    {
        char *pos = data;
        char *end = pos + size - 1;
        char temp;

        for (; pos < end; pos++, end--)
        {
            temp = *pos;
            *pos = *end;
            *end = temp;
        }
    }
}



/**
 * 向文件内写入bin数据
 * @param *fp
 * @param *data
 * @param size
 * @param reverse
 * @return 
 */
static int L_FileWriteBinData(FILE *fp, const char *data, int size, Bool reverse)
{
    int result = 0;

    if (reverse)
    {
        char *temp_buff = malloc(size);
        if (temp_buff != NULL)
        {
            memcpy(temp_buff, data, size);
            L_ReverseBin(temp_buff, size);
            result = fwrite(temp_buff, 1, size, fp);

            free(temp_buff);
        }
        else
        {
            // char *pos = data;
            char *end = data + size - 1;
            // int i;

            for (; end >= data; end--)
            {
                if (fwrite(end, 1, 1, fp) != 1)
                    break;
            }
            
            result = size - (end - data + 1);
        }
    }
    else
    {
        result = fwrite(data, 1, size, fp);
    }


    return result;
}


/**
 * 写头部信息. 外层保证fp有效
 * @param *fp
 * @param *head
 * @return 
 */
int HeadInfoWrite(FILE *fp, THeadInfo *head)
{   
    fwrite(head->reserve_, 1, sizeof(head->reserve_), fp);

    return ftell(fp);
}


/**
 * 
 * @param *fp
 * @param *filename
 * @return 返回当前fp seek位置
 */
int SrcFileWrite(FILE *fp, const char *filename)
{
    FILE *src_fp = fopen(filename, "rb");

    if (src_fp != NULL)
    {
        int size;

        fseek(src_fp, 0, SEEK_SET);

        size = fread(g_wr_mem, 1, sizeof(g_wr_mem), src_fp);

        while (size > 0)
        {
            if (fwrite(g_wr_mem, 1, size, fp) != size)
            {
                printf("fwrite failed, file size: %d Byte >> src_filename:%s !!!\n", size, filename);
                break;
            }
            else if (size != sizeof(g_wr_mem))
            {
                printf("fwrite ok +%d Byte >> src_filename:%s !!!\n", size, filename);
                break;
            }

            size = fread(g_wr_mem, 1, sizeof(g_wr_mem), src_fp);
        }

        fclose(src_fp);
    }

    return ftell(fp);
}





void __cdecl switch_to_current_folder() 
{
    char exe_path_unicode[MAX_PATH] = { 0 };
    char exe_path[MAX_PATH] = {0};
    char *pos = exe_path;
    char *end = pos + MAX_PATH - 1;
    char *p = pos;
    int len = GetModuleFileName(0, exe_path_unicode,sizeof(exe_path_unicode));
    int i;

    for (i = 0; i < len; i++)
    {
        exe_path[i] = exe_path_unicode[i * 2];
    }

    // WideCharToMultiByte(CP_ACP, 0, 2, exe_path, len, exe_path, sizeof(exe_path), '0');
    // printf("%s\n", exe_path);

    for (; pos < end; pos++)
    {
        if (*pos == '\\' || *pos == '/')
            p = pos + 1;
    }

    *p = '\0';
    len = (p - exe_path) * 2;
    printf("exe_path: %s \n", exe_path);

    exe_path_unicode[len] = '\0';

    SetCurrentDirectory(exe_path_unicode);
}

int main(int argc, char *argv[])
{
    switch_to_current_folder();
    // 头部信息转换
    LoadHeadInfo(&g_head_info_);

    // 识别生成文件信息
    char *output_filename = L_ReadCfgStr_BuildFilename();
    
    // 生成文件  创建
    FILE *output_fp = fopen(output_filename, "wb");

    if (output_fp != NULL)
    {
        fseek(output_fp, 0, SEEK_SET);

        // 写入头部信息
        HeadInfoWrite(output_fp, &g_head_info_);

        // 追加 源文件
        unsigned char index = 0;
        char *ref_filename = L_ReadCfgStr_RefFilename(index);

        while (ref_filename != NULL)
        {
            SrcFileWrite(output_fp, ref_filename);
            
            index++;
            ref_filename = L_ReadCfgStr_RefFilename(index);
        }
        
        // 输出结果
        printf("End file_joint, output filesize: %d byte, filename: %s !!!", ftell(output_fp), output_filename);
        fclose(output_fp);
    }
    else
    {
        printf("End file_joint, output file create failed !!!");
    }

    return 0;
}
